#' @docType class
#' @title {{classname}}
#'
#' @description {{classname}} Class
#'
#' @format An \code{R6Class} generator object
#'
#' @importFrom R6 R6Class
#' @importFrom jsonlite fromJSON toJSON
#' @export
{{classname}} <- R6::R6Class(
  "{{classname}}",
  public = list(
    #' @field actual_instance the object stored in this instance.
    actual_instance = NULL,
    #' @field actual_type the type of the object stored in this instance.
    actual_type = NULL,
    #' @field one_of  a list of types defined in the oneOf schema.
    one_of = list({{#oneOf}}"{{{.}}}"{{^-last}}, {{/-last}}{{/oneOf}}),
    #' Initialize a new {{{classname}}}.
    #'
    #' @description
    #' Initialize a new {{{classname}}}.
    #'
    #' @param instance an instance of the object defined in the oneOf schemas: {{#oneOf}}"{{{.}}}"{{^-last}}, {{/-last}}{{/oneOf}}
    #' @export
    initialize = function(instance = NULL) {
      if (is.null(instance)) {
        # do nothing
      } {{#oneOf}}else if (get(class(instance)[[1]], pos = -1)$classname ==  "{{{.}}}") {
        self$actual_instance <- instance
        self$actual_type <- "{{{.}}}"
      } {{/oneOf}}else {
        stop(paste("Failed to initialize {{{classname}}} with oneOf schemas {{#oneOf}}{{{.}}}{{^-last}}, {{/-last}}{{/oneOf}}. Provided class name: ",
                   get(class(instance)[[1]], pos = -1)$classname))
      }
    },
    #' Deserialize JSON string into an instance of {{{classname}}}.
    #'
    #' @description
    #' Deserialize JSON string into an instance of {{{classname}}}.
    #' An alias to the method `fromJSON` .
    #'
    #' @param input The input JSON.
    #' @return An instance of {{{classname}}}.
    #' @export
    fromJSONString = function(input) {
      self$fromJSON(input)
    },
    #' Deserialize JSON string into an instance of {{{classname}}}.
    #'
    #' @description
    #' Deserialize JSON string into an instance of {{{classname}}}.
    #'
    #' @param input The input JSON.
    #' @return An instance of {{{classname}}}.
    #' @export
    fromJSON = function(input) {
      matched <- 0 # match counter
      matched_schemas <- list() #names of matched schemas
      error_messages <- list()
      instance <- NULL

      {{#useOneOfDiscriminatorLookup}}
      {{#discriminator}}
      oneof_lookup_result <- tryCatch({
          discriminatorValue <- (jsonlite::fromJSON(input, simplifyVector = FALSE))$`{{{propertyBaseName}}}`
          if (is.null(discriminatorValue)) { # throw error if it's null
            stop("Error! The value of the discriminator property `{{{propertyBaseName}}}`, which should be the class type, is null")
          }
          switch(discriminatorValue,
          {{#mappedModels}}
          {{{mappingName}}}={
            {{{modelName}}}$public_methods$validateJSON(input)
            {{{modelName}}}_instance <- {{{modelName}}}$new()
            self$actual_instance <- {{{modelName}}}_instance$fromJSON(input)
            self$actual_type <- "{{{modelName}}}"
            return(self)
          }{{^-last}},{{/-last}}{{#-last}})},{{/-last}}
          {{/mappedModels}}
          {{^mappedModels}}
          {{#oneOf}}
          {{{.}}}={
            {{{.}}}$public_methods$validateJSON(input)
            {{{.}}}_instance <- {{{.}}}$new()
            self$actual_instance <- {{{.}}}_instance$fromJSON(input)
            self$actual_type <- "{{{.}}}"
            return(self)
          }{{^-last}},{{/-last}}{{#-last}})},{{/-last}}
          {{/oneOf}}
          {{/mappedModels}}
          error = function(err) err
      )
      if (!is.null(oneof_lookup_result["error"])) {
        error_messages <- append(error_messages, sprintf("Failed to lookup discriminator value for {{classname}}. Error message: %s. JSON input: %s", oneof_lookup_result["message"], input))
      }

      {{/discriminator}}
      {{/useOneOfDiscriminatorLookup}}
      {{#composedSchemas.oneOf}}
      {{^isNull}}
      `{{{dataType}}}_result` <- tryCatch({
          {{#isPrimitiveType}}
          instance <- jsonlite::fromJSON(input, simplifyVector = FALSE)
          if (typeof(instance) != "{{{dataType}}}") {
            stop(sprintf("Data type doesn't match. Expected: %s. Actual: %s.", "{{{dataType}}}", typeof(instance)))
          }
          {{/isPrimitiveType}}
          {{^isPrimitiveType}}
          `{{{dataType}}}`$public_methods$validateJSON(input)
          `{{{dataType}}}_instance` <- `{{{dataType}}}`$new()
          instance <- `{{{dataType}}}_instance`$fromJSON(input)
          {{/isPrimitiveType}}
          instance_type <- "{{{dataType}}}"
          matched_schemas <- append(matched_schemas, "{{{dataType}}}")
          matched <- matched + 1
        },
        error = function(err) err
      )

      if (!is.null(`{{{dataType}}}_result`["error"])) {
        error_messages <- append(error_messages, `{{{dataType}}}_result`["message"])
      }

      {{/isNull}}
      {{/composedSchemas.oneOf}}
      if (matched == 1) {
        # successfully match exactly 1 schema specified in oneOf
        self$actual_instance <- instance
        self$actual_type <- instance_type
      } else if (matched > 1) {
        # more than 1 match
        stop(paste("Multiple matches found when deserializing the input into {{{classname}}} with oneOf schemas {{#oneOf}}{{{.}}}{{^-last}}, {{/-last}}{{/oneOf}}. Matched schemas: ",
                   paste(matched_schemas, collapse = ", ")))
      } else {
        # no match
        stop(paste("No match found when deserializing the input into {{{classname}}} with oneOf schemas {{#oneOf}}{{{.}}}{{^-last}}, {{/-last}}{{/oneOf}}. Details: >>",
                   paste(error_messages, collapse = " >> ")))
      }

      self
    },
    #' Serialize {{{classname}}} to JSON string.
    #'
    #' @description
    #' Serialize {{{classname}}} to JSON string.
    #'
    #' @return JSON string representation of the {{{classname}}}.
    #' @export
    toJSONString = function() {
      if (!is.null(self$actual_instance)) {
        as.character(jsonlite::minify(self$actual_instance$toJSONString()))
      } else {
        NULL
      }
    },
    #' Serialize {{{classname}}} to JSON.
    #'
    #' @description
    #' Serialize {{{classname}}} to JSON.
    #'
    #' @return JSON representation of the {{{classname}}}.
    #' @export
    toJSON = function() {
      if (!is.null(self$actual_instance)) {
        self$actual_instance$toJSON()
      } else {
        NULL
      }
    },
    #' Validate the input JSON with respect to {{{classname}}}.
    #'
    #' @description
    #' Validate the input JSON with respect to {{{classname}}} and
    #' throw exception if invalid.
    #'
    #' @param input The input JSON.
    #' @export
    validateJSON = function(input) {
      # backup current values
      actual_instance_bak <- self$actual_instance
      actual_type_bak <- self$actual_type

      # if it's not valid, an error will be thrown
      self$fromJSON(input)

      # no error thrown, restore old values
      self$actual_instance <- actual_instance_bak
      self$actual_type <- actual_type_bak
    },
    #' Returns the string representation of the instance.
    #'
    #' @description
    #' Returns the string representation of the instance.
    #'
    #' @return The string representation of the instance.
    #' @export
    toString = function() {
      jsoncontent <- c(
        sprintf('"actual_instance": %s', if (is.null(self$actual_instance)) NULL else self$actual_instance$toJSONString()),
        sprintf('"actual_type": "%s"', self$actual_type),
        sprintf('"one_of": "%s"', paste(unlist(self$one_of), collapse = ", "))
      )
      jsoncontent <- paste(jsoncontent, collapse = ",")
      as.character(jsonlite::prettify(paste("{", jsoncontent, "}", sep = "")))
    },
    #' Print the object
    #'
    #' @description
    #' Print the object
    #'
    #' @export
    print = function() {
      print(jsonlite::prettify(self$toJSONString()))
      invisible(self)
    }
  ),
  # Lock the class to prevent modifications to the method or field
  lock_class = TRUE
)
## Uncomment below to unlock the class to allow modifications of the method or field
#{{classname}}$unlock()
#
## Below is an example to define the print function
#{{classname}}$set("public", "print", function(...) {
#  print(jsonlite::prettify(self$toJSONString()))
#  invisible(self)
#})
## Uncomment below to lock the class to prevent modifications to the method or field
#{{classname}}$lock()
